package com.dongnao.network.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleStateHandler;

import java.util.concurrent.TimeUnit;


public class NettyServer {

    public static void main(String[] args) {

        //创建BossGroup 和WorkerGroup
        //1.创建两个线程组bossGroup和 workerGroup
        //2.bossGroup只是处理连接请求，真正的和客户端业务处理，会交给 workerGroup完成
        //3．两个都是无限循环
        //4.bossGroup和 workerGroup含有的子线程(NioEventLoop)的个数默认为cpu核数*2
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();//不指定的话个数默认为cpu核数*2

        try {
            //ServerBootStrap本质上是一个容器，放置各种网络服务配置，然后组合起来
            ServerBootstrap bootstrap = new ServerBootstrap();

            //链式编程设置配置信息
            bootstrap.group(bossGroup, workerGroup)//设置两个线程组
                    .channel(NioServerSocketChannel.class)//使用NioServerSocketChannel作为服务器通道的实现
                    .option(ChannelOption.SO_BACKLOG, 128)//设置线程队列的连接个数//设置服务端可连接队列大小
                    .childOption(ChannelOption.SO_KEEPALIVE, true)//设置保持活动的连接个数
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new ChannelInitializer<SocketChannel>() {//使用匿名方式创建一个通道测试对象
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //可以使用一个集合管理SocketChannel，推送消息时将业务加入到每个channel对应的NIOEventLoop的任务队列上
                            //如：ch.eventLoop().schedule()
                            System.out.println("客户端通道 " + ch.hashCode());
                            //ChannelPipeline是个双向链表，把ChannelHandler包装成ChannelHandlerContext加入其中

                            //IdleStateHandler是Netty提供的空闲状态的处理器
                            //第一二三个参数分别表示多长时间没有读、写、读写后发送一个心跳检测包监测是否连接
                            ch.pipeline().addLast(new IdleStateHandler(3, 5, 30, TimeUnit.SECONDS));
                            ch.pipeline().addLast(new NettyServerHandler());
                            ch.pipeline().addLast(new NettyServerHandler());
                            ch.pipeline().addLast(new NettyServerHandlerTask());
                            ch.pipeline().addLast(new IdleHandler());
                        }
                    });

            System.out.println("服务器开始启动..." + System.currentTimeMillis());
            //api是基于异步的事件驱动，需要做同步
            ChannelFuture cf = bootstrap.bind(8899).sync();
            System.out.println("ChannelFuture: " + cf + ", " + System.currentTimeMillis());
            cf.addListener(new ChannelFutureListener() {
                @Override
                public void operationComplete(ChannelFuture future) throws Exception {
                    if (future.isSuccess()) {
                        System.out.println("服务器启动成功，ChannelFuture：" + future + ", " + System.currentTimeMillis());
                    }
                }
            });

            //对关闭通道进行监听//等待服务端channel关闭
            cf.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            //释放资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
